#pragma once

#include <iostream>
#include <string>
#include <ctime>
#include <cstdarg>
#include <unistd.h>
#include <fstream>

using namespace std;

#define FILENAME "LOG.txt" // 保存LOG的文件名

bool IsSave = false; // 标记是否需要保存到文件

// LOG(DEBUG, " Content is :%s %d %f ", "helloworld", 10, 3.14);
#define LOG(level, format, ...)                                \
    do                                                         \
    {                                                          \
        log(level, __FILE__, __LINE__, format, ##__VA_ARGS__); \
    } while (0)

// 两各接口，修改IsSave的值，便于外部修改
#define EnIsSave()     \
    do                 \
    {                  \
        IsSave = true; \
    } while (0)

#define EnIsPrint()     \
    do                  \
    {                   \
        IsSave = false; \
    } while (0)

// 日志等级枚举
enum Level
{
    DEBUG = 0,
    INFO,
    WARNING,
    ERROR,
    FATAL
};

// 将日志等级转为字符串
string LevelToString(int level)
{
    switch (level)
    {
    case DEBUG:
        return "DEBUG";
    case INFO:
        return "INFO";
    case WARNING:
        return "WARNING";
    case ERROR:
        return "ERROR";
    case FATAL:
        return "FATAL";
    default:
        return "UNKNOW";
    }
}

// 将时间转为字符串
string timeToString(struct tm *stm)
{
    char timebuffer[64];
    snprintf(timebuffer, sizeof(timebuffer), "%d-%d-%d %d:%d:%d",
             stm->tm_year + 1900, stm->tm_mon + 1, stm->tm_mday,
             stm->tm_hour, stm->tm_min, stm->tm_sec);

    return timebuffer;
}

// 如果是多线程打印日志，打印到显示器，显示器是公共资源（临界资源），需要保护
pthread_mutex_t Mutex = PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP;

// 将日志存放到文件中，利用的C++的文件读写操作
void IsSaveFile(string &message)
{
    ofstream out(FILENAME, ios::app);
    if (!out)
    {
        return;
    }
    out << message << endl;

    out.close();
}

//  日志的等级 时间 文件 行号 日志内容
void log(int level, string filename, int line, const char *format, ...)
{
    // 等级
    string levelstr = LevelToString(level);

    // 时间
    time_t curtime = time(nullptr);
    struct tm stm;
    localtime_r(&curtime, &stm);
    string timestr = timeToString(&stm);

    // 日志内容，多参数
    va_list args;
    va_start(args, format);
    char ContentStr[1024];
    vsnprintf(ContentStr, sizeof(ContentStr), format, args);
    va_end(args);

    // 将日志放到一个string里面
    string message = "[ " + levelstr + " ]  [ " + timestr + " ]  [ " + filename + " ]  [ " + to_string(line) + " ]  [ " + ContentStr + " ]" + "\0";

    // 保护临界资源
    // 加锁
    pthread_mutex_lock(&Mutex);
    if (!IsSave)
    {
        cout << "[ "
             << levelstr << " ]  [ "
             << timestr << " ]  [ "
             << filename << " ]  [ "
             << line << " ]  [ "
             << ContentStr << " ]"
             << endl;
    }
    else
    {
        IsSaveFile(message);
    }
    // 释放锁
    pthread_mutex_unlock(&Mutex);
}
